const puppeteer = require('puppeteer')
const devices = require('puppeteer/DeviceDescriptors') //puppeteer 提供了一些设备的参数选项
const { sleep , genScriptContent } = require('./util/utils') //公共工具方法
const scriptFns = require('./util/browserUtils')

const skeleton = async function(url, option = {}) {

  const defaultOption = {
    device: 'iPhone 6'
  }

  const { 
    device, 
    defer = 0, 
    remove = [], 
    excludes = [], 
    hide= [],
    launch: launchOpt
 } = Object.assign({}, defaultOption, option)

  // 当 Puppeteer 连接到一个 Chromium 实例的时候会通过 puppeteer.launch 或 puppeteer.connect 创建一个 Browser 对象。
  // 返回一个新的 [Page] 对象。[Page] 在一个默认的浏览器上下文中被创建。
  const browser = await puppeteer.launch(launchOpt) 

  const page = await browser.newPage() //新建一个页面

  /**
   * 根据指定的参数和 user agent 生成模拟器。此方法是和下面两个方法效果相同
   * @param { options }
   * viewport <[Object]>
        width <[number]> 页面的宽度，单位像素.
        height <[number]> 页面的高度，单位像素.
        deviceScaleFactor <[number]> 定义设备缩放， (类似于 dpr). 默认 1。
        isMobile <[boolean]> 要不要包含meta viewport 标签. 默认 false。
        hasTouch<[boolean]> 指定终端是否支持触摸。默认 false
        isLandscape <[boolean]> 指定终端是不是 landscape 模式. 默认 false。
      userAgent <[string]>
   * 
   */
  await page.emulate(devices[device])
  
  await page.goto(url)

  // 将一些 utils 插入到打开的页面执行环境中
  await page.addScriptTag({
    content: genScriptContent(...scriptFns)
  })

  await sleep(defer)
/**
 * page.evaluate(pageFunction, ...args)
 * pageFunction <[function]|[string]> 要在页面实例上下文中执行的方法
    ...args <...[Serializable]|[JSHandle]> 要传给 pageFunction 的参数
    返回: <[Promise]<[Serializable]>> pageFunction执行的结果
 */
  const html = await page.evaluate(async ( remove, excludes, hide ) => { 
    //为什么用bind？
    const $ = document.querySelectorAll.bind(document)

    if (remove.length) {
      const removeEle = $(remove.join(','))
      Array.from(removeEle).forEach(ele => ele.parentNode.removeChild(ele))
    }

    if (hide.length) {
      const hideEle = $(hide.join(','))
      Array.from(hideEle).forEach(ele => ele.style.opacity = 0)
    }

    const excludesEle = excludes.length ? Array.from($(excludes.join(','))) : []

    await traverse(document.documentElement, excludesEle)

    return document.documentElement.outerHTML

  }, remove, excludes,hide)

  // browser.close()

  return { html }
}

module.exports = skeleton
